import {useState, useEffect, useCallback, useMemo} from 'react';

function compSign(A, B) {
  return A > B ? 1 : A < B ? -1 : 0;
}

function getProp(object, propPath='') {
  const path = propPath.split('.').reverse();

  let curr = object;
  while(curr && path.length > 0) {
    curr = curr?.[path.pop()];
  }

  return curr;
}

function applyDataView(origData, colFilters, colSorts) {
  
  let data = origData;

  for (let key of Object.keys(colFilters)){
    data = data.filter(elem => {
      return getProp(elem, key)?.toString().includes(colFilters[key]);
    })
  }

  for (let key of Object.keys(colSorts)){
    data.sort(
      (elemA, elemB) => {
        const valA = getProp(elemA, key);
        const valB = getProp(elemB, key);
        return colSorts[key] === 'descending'
        ? compSign(valB, valA)
        : compSign(valA, valB)
      }
    );
  }

  return data;
}

const useEdit = function(origData, schema, nextLevelKey='succ') {

  const [path, setPath] = useState([]);
  const [list, setList] = useState(origData);

  const [colFilters, setColFilters] = useState({});
  const [colSorts, setColSorts] = useState({});

  useEffect(() => {
    setPath([]);
    setList(origData);

    setColFilters({});
    setColSorts({});

  }, [origData]);

  const assocNewList = (list) => {
    const newList = [...list];
    if(path.length > 0) {
      path[path.length - 1][nextLevelKey] = newList;
    }
    setList(newList)
  };

  const setFilter = (key, pattern) => {
    console.log('FILTER', pattern, 'APPLIED TO', key);
    if (pattern === '' || pattern === undefined) {
      const {[key]:_, ...restFilters} = colFilters;
      setColFilters(restFilters);
    } else {
      setColFilters({...colFilters, [key]: pattern});
    }
  }

  const setSorter = (key) => {
    const order = colSorts[key];
    const nextOrder = order === 'ascending'
    ? 'descending'
    : order === 'descending'
    ? undefined
    : 'ascending'
    
    if (nextOrder === undefined) {
      setColFilters({});
    } else {
      setColSorts({[key]: nextOrder})
    }
  }
  
  const enter = ({index}) => {

    const record = list[index];
    const nextLevel = record[nextLevelKey];

    setPath([...path, record]);
    setList(nextLevel);
  };

  const leave = useCallback(({index}) => {

    const newHist = path.slice(0, index);
    setPath(newHist);

    if (newHist.length === 0){
      setList(origData);
    } else {
      const lastElem = newHist.slice(-1)[0];
      setList(lastElem.succ);
    }
  }, [origData, schema]);

  const update = useCallback(({index}, newRec) => {
    list.splice(index, 1, newRec);
    console.log('update', list);
    assocNewList(list);
  }, [list]);

  const insert = useCallback(({index}) => {
    const newRec = {'new-created-entry': true};
    list.splice(index + 1, 0, newRec);
    assocNewList(list);
  }, [list]);

  const remove = useCallback(({index}) => {
    list.splice(index, 1);
    assocNewList(list);
  }, [list]);

  const swapFore = useCallback(({index}) => {
    if (index < list.length - 1) {
      const [orig, next] = list.splice(index, 2);
      list.splice(index, 0, next, orig);
      assocNewList(list);
    }
  }, [list]);

  const swapBack = useCallback(({index}) => {
    if (index > 0) {
      const [orig, next] = list.splice(index - 1, 2);
      list.splice(index - 1, 0, next, orig);
      assocNewList(list);
    }
  }, [list]);

  const filterApplied = useMemo(() => {
    const filterLen = Object.keys(colFilters).length;
    const sortsLen = Object.keys(colSorts).length;
    return !(filterLen === 0 && sortsLen === 0);
  }, [colFilters, colSorts]);

  const data = !filterApplied ? list : applyDataView(list, colFilters, colSorts);

  return {
    path,
    data,
    setSorter,
    setFilter,
    enter,
    leave,
    insert,
    remove,
    update,
    swapBack,
    swapFore
  }
}

export default useEdit;